﻿
namespace SharePoint.ImportExport.Core
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Reflection;
    
    public class ServerConnectionInformation
    {
        #region internalPropertyStores
        /// <summary>
        /// Internal provider for the host name used to connect to server
        /// </summary>
        private string _hostname = string.Empty;
        
        /// <summary>
        /// internal store for the protocol string. Typically like 'https://'
        /// </summary>
        private string _protocol = string.Empty;

        /// <summary>
        /// Type of server, starts out as unknown.
        /// </summary>
        private ServerType _serverType = ServerType.Undetermined;

        /// <summary>
        /// How is the connection string created from various pieces, using delimiters?
        /// </summary>
        private string _connectionStringDelimiter = "/";

        /// <summary>
        /// This is the top level structural identifier object. Typically for sharepoint 2007 at least this would be managed path.
        /// 'sites' and 'mysites' and '/' also known as root would be this. 
        /// <remarks>Special case singular marks that are the same as delimiter, leave this empty as it should be populated between parts by the
        /// delimiter.</remarks>
        /// </summary>
        private string _topStructuralIdentifier = string.Empty;

        /// <summary>
        /// This is the midlevel structural identifier. Typically for sharepoint this would be sitecollection level.
        /// 'companysites' is an example, if that were not the managed path itself
        /// </summary>
        private string _midStructuralIdentifier = string.Empty;

        /// <summary>
        /// This is a string representation of the remainder identifier part. For sharepoint this would be spwebs linked after each other,
        /// without first and last slashes. Such as 'marketing/publications/procedures'
        /// </summary>
        private string _remainingStructuralIdentifier = string.Empty;

        /// <summary>
        /// In the case a user of this class knows only the string but not what it consists of, we must allow usage. Not nice, but life frequently isn't 
        /// and code should not be blocking.
        /// </summary>
        private string _justAnUglyUnknownString = string.Empty;

        #endregion

        #region overRides
        public override string ToString()
        {
            return string.Format("[ServerConnection(servertype:'{0}')]:{1}", _serverType.ToString(), ConnectionString());
        }
        #endregion

        #region publicAccessors
        /// <summary>
        /// Contains the usable (accessible) host name for our server connection point.
        /// </summary>
        public string AccessibleHostName { 
            get { return _hostname;}
            set { _hostname = value; }
        }

        /// <summary>
        /// Contains the protocol string for our connection. Http:// or sts:// etc
        /// </summary>
        public string Protocol
        {
            get { return _protocol; }
            set { _protocol = value; }
        }

        /// <summary>
        /// Lists the current server type. Starts out as unknown.
        /// </summary>
        public ServerType ServerType
        {
            get { return _serverType; }
            set { _serverType = value; }
        }

        /// <summary>
        /// The connection string delimiter used to create the connection string from parts.
        /// </summary>
        public string ConnectionStringDelimiter
        {
            get { return _connectionStringDelimiter; }
            set { _connectionStringDelimiter = value; }
        }

        /// <summary>
        /// The top level structural identified for the connection string. Separated due to its use in other than string operations.
        /// Functionally for sharepoint it is the managed path.
        /// </summary>
        public string TopStructuralIdentifier
        {
            get { return _topStructuralIdentifier; }
            set { _topStructuralIdentifier = value; }
        }

        /// <summary>
        /// Middle level structural identifier string. Functionally separated, as for some server types this is used for operations other than connection stirng.
        /// For sharepoint this would be the site collection.
        /// </summary>
        public string MidLevelStructuralIdentifier
        {
            get { return _midStructuralIdentifier; }
            set { _midStructuralIdentifier = value; }
        }

        /// <summary>
        /// The remaining structural identification information.
        /// For most systems this would be a folder/library identification structure.
        /// For sharepoint this is the part after site collection, in other words the web structures.
        /// </summary>
        public string RemainingStructuralIdentifier
        {
            get { return _remainingStructuralIdentifier; }
            set { _remainingStructuralIdentifier = value; }
        }

        /// <summary>
        /// This is for the case that a user of this class knows the string to connect but nothing further.
        /// This is of course not recommended, but provided for those cases when time overrides the price of doing things right.
        /// You know who you are....
        /// <remarks>Don't use. Really.</remarks>
        /// </summary>
        public string JustAConnectionString
        {
            get { return _justAnUglyUnknownString; }
            set { _justAnUglyUnknownString = value; }
        }

        #endregion

        #region constructors
        /// <summary>
        /// This is the main constructor. If the Mid or remaining levels are not used, just enter empty strings and it shall be fine.
        /// </summary>
        /// <param name="Protocol">Protocol used to connect to the server.</param>
        /// <param name="Hostname">The access host name. Can be a dns pointer, true hostname, depending on protocol really. Needs to be actually accessible.</param>
        /// <param name="TopStructuralIdentifier">Top level identifier, such as manager paths for sharepoint.</param>
        /// <param name="MidLevelStructuralIdentifier">Middle level identifier, such as site collection for sharepoint.</param>
        /// <param name="RemainingStructuralIdentifier">Further structural pointer, such as folder/folder/folder</param>
        /// <param name="Delimiter">The connection string building and splitting identifier. Typically forward slash.</param>
        /// <example>ServerConnectionInformation test = new ServerConnectionInformation("http://", "myserver", "sites", string.empty, "companysite/marketing", "/")</example>
        public ServerConnectionInformation(string Protocol, string Hostname, string TopStructuralIdentifier,
            string MidLevelStructuralIdentifier, string RemainingStructuralIdentifier, string Delimiter)
        {
            _protocol = Protocol;
            _hostname = Hostname;
            _topStructuralIdentifier = TopStructuralIdentifier;
            _midStructuralIdentifier = MidLevelStructuralIdentifier;
            _remainingStructuralIdentifier = RemainingStructuralIdentifier;
            _connectionStringDelimiter = Delimiter;
        }


        /// <summary>
        /// Plain override that USES the full 6 parameter one, passing empty strings for non-provided values.
        /// </summary>
        /// <param name="Protocol">The specified communications protocol string.</param>
        /// <param name="HostName">The server connection point that we are using for access.</param>
        /// <param name="Delimiter">String delimiter for connection string.</param>
        public ServerConnectionInformation(string Protocol, string HostName, string Delimiter)
            : this(Protocol, HostName, string.Empty, string.Empty, string.Empty, Delimiter)
        {
        }

        /// <summary>
        /// For some server types, we allow omitting protocol, and will use the defaultprotocol for said server.
        /// Delimiter string will also be used per the default protocol.
        /// We also omit at initialization time everything but hostname.
        /// </summary>
        /// <param name="HostName"></param>
        /// <param name="ServerSpecification"></param>
        /// <exception cref="SPExportException">throws an exception if tried for not supported server types</exception>
        public ServerConnectionInformation(string HostName, ServerType ServerSpecification)
        {
            System.Diagnostics.Debug.Print(ServerSpecification.ToString());
            System.Diagnostics.Debug.Assert(false, "aa");
            _hostname = HostName;
            switch (ServerSpecification)
            {
                case ServerType.Sharepoint2003:
                    {
                        _connectionStringDelimiter = "/";
                        _protocol = "http://";
                        _serverType = ServerType.Sharepoint2003;
                        break;
                    }
                case ServerType.Sharepoint2010:
                    {
                        _connectionStringDelimiter = "/";
                        _protocol = "http://";
                        _serverType = ServerType.Sharepoint2010;
                        break;
                    }
                case ServerType.Sharepoint2007:
                    {
                        _connectionStringDelimiter = "/";
                        _protocol = "http://";
                        _serverType = ServerType.Sharepoint2007;
                        break;
                    }

                case ServerType.Undetermined:
                    
                case ServerType.DominoUndetermined:
                    
                case ServerType.Domino4:
                    
                case ServerType.Domino5:
                    
                case ServerType.Domino6:
                    
                case ServerType.Domino7:
                    
                case ServerType.Domino8:
                    
                case ServerType.ExchangeUndetermined:
                    
                case ServerType.Exchange4:
                    
                case ServerType.Exchange5:
                    
                case ServerType.Exchange2000:
                    
                case ServerType.Exchange2003:
                    
                case ServerType.Exchange2007:
                    
                case ServerType.Exchange2010:
                    
                case ServerType.WSSUndetermined:
                    
                case ServerType.WSSSTS:
                    
                case ServerType.WSS2:
                    
                case ServerType.WSS3:
                    
                case ServerType.SharepointUndetermined:
                    
                case ServerType.SharepointSPS:
                default:
                    throw new SPExportException(string.Format("Unable to initialize ServerConnectionType object," +
                        "implementation missing for selected server type({0})", ServerSpecification));
                    
            }
        }

        /// <summary>
        /// Overload to create and copy from original.
        /// </summary>
        /// <param name="original"></param>
        /// <param name="relativeTargetWebPath"></param>
        public ServerConnectionInformation(ServerConnectionInformation original, string relativeTargetWebPath):this(original.Protocol, original.AccessibleHostName,
            original.TopStructuralIdentifier, original.MidLevelStructuralIdentifier, relativeTargetWebPath, original.ConnectionStringDelimiter)
        {

        }
        /// <summary>
        /// This will be used when server type is known, and the target servers root url in the case of sharepoint is known
        /// </summary>
        /// <param name="protocolAndHostname">Typically protocol + splitter + server, like http://myserver</param>
        /// <param name="ServerSpecification">Server type. This function may throw an exception on unsupported server types</param>
        /// <param name="protocolSplitter">typically // for most intarweb protocols</param>
        public ServerConnectionInformation(string protocolAndHostname, string protocolSplitter,ServerType ServerSpecification)
        {
            this.ServerType = ServerSpecification;
            this.splitHostnameAndProtocol(protocolAndHostname, protocolSplitter, out _hostname, out _protocol, true);
        }

        public ServerConnectionInformation(string WholeConnectionString)
        {
            _justAnUglyUnknownString = WholeConnectionString;
        }

        #endregion

        #region functions
        /// <summary>
        /// Returns the formatted connection string. Generic. May require a server type to be set.
        /// </summary>
        /// <returns></returns>
        public string ConnectionString()
        {
            if (!string.IsNullOrEmpty(_justAnUglyUnknownString))
                return _justAnUglyUnknownString;

            StringBuilder retVal = new StringBuilder();
            retVal.Append(_protocol);
            retVal.Append(_hostname);
            
            if (!string.IsNullOrEmpty(_topStructuralIdentifier) && !_topStructuralIdentifier.Equals(_connectionStringDelimiter,StringComparison.InvariantCultureIgnoreCase))
            {
                
                //topstructuralidentifier is not the same as pure delimiter now...
                if (!_topStructuralIdentifier.StartsWith(_connectionStringDelimiter))
                {
                    retVal.Append(_connectionStringDelimiter); //since the hostname should not contain delimiter, append a delimiter before the structural identifier
                }
                
                
                    //append the non null topstructuralidentifier on the return value
                    retVal.Append(_topStructuralIdentifier);
                
            }
            
            if(!string.IsNullOrEmpty(_midStructuralIdentifier))
            {
                if (!_midStructuralIdentifier.StartsWith(_connectionStringDelimiter))
                    retVal.Append(_connectionStringDelimiter);//prepend with delimiter before MID
                retVal.Append(_midStructuralIdentifier);
            }
            if(!string.IsNullOrEmpty(_remainingStructuralIdentifier))
            {
                if(!_remainingStructuralIdentifier.StartsWith(_connectionStringDelimiter))
                    retVal.Append(_connectionStringDelimiter); //prepend with delimiter before Remaining
                retVal.Append(_remainingStructuralIdentifier);
            }
            return retVal.ToString();
        }

        /// <summary>
        /// Returns the Top Level Connection String
        /// </summary>
        /// <param name="Server"></param>
        /// <returns></returns>
        public string ToplevelConnectionString(ServerType Server)
        {
            StringBuilder retVal = new StringBuilder();
            retVal.Append(RootConnectionString(Server));
            string returnValue = String.Empty;
            switch (Server)
            {
                case ServerType.Sharepoint2003:
                case ServerType.Sharepoint2007:
                case ServerType.Sharepoint2010:
                    {
                        if (!string.IsNullOrEmpty(_topStructuralIdentifier))
                        {
                            retVal.Append(_connectionStringDelimiter + _topStructuralIdentifier);
                        }

                        if (!string.IsNullOrEmpty(_midStructuralIdentifier))
                        {
                            retVal.Append(_connectionStringDelimiter + _midStructuralIdentifier);
                        }

                        returnValue = retVal.ToString();

                        break;
                    }
                default:
                    {
                        throw new SPExportException(string.Format("This function not supported for server type {0},  Top level connection string.",
                            Server));
                    }
            }
            return returnValue;
        }

        public string RelativeConnectionString(ServerType Server)
        {
            StringBuilder retVal = new StringBuilder();
            string returnValue = String.Empty;
            switch (Server)
            {
                case ServerType.Sharepoint2003:
                case ServerType.Sharepoint2007:
                case ServerType.Sharepoint2010:
                    {
                        if (!string.IsNullOrEmpty(_topStructuralIdentifier))
                        {
                            retVal.Append(_connectionStringDelimiter + _topStructuralIdentifier);
                        }

                        if (!string.IsNullOrEmpty(_midStructuralIdentifier))
                        {
                            retVal.Append(_connectionStringDelimiter + _midStructuralIdentifier);
                        }

                        returnValue = retVal.ToString();

                        break;
                    }
                default:
                    {
                        throw new SPExportException(string.Format("This function not supported for server type {0},  Top level connection string.",
                            Server));
                    }
            }
            return returnValue;
        }


        /// <summary>
        /// Returns the root connection string
        /// </summary>
        /// <param name="Server"></param>
        /// <returns></returns>
        public string RootConnectionString(ServerType Server)
        {
            string returnValue = String.Empty;
            switch (Server)
            {
                case ServerType.Sharepoint2003:
                case ServerType.Sharepoint2007:
                case ServerType.Sharepoint2010:
                    {
                        StringBuilder retVal = new StringBuilder();
                        retVal.Append(_protocol);
                        retVal.Append(_hostname);

                        returnValue = retVal.ToString();

                        break;
                    }
                default:
                    {
                        throw new SPExportException(string.Format("This function not supported for server type {0},  Top level connection string.",
                            Server));
                    }
            }
            return returnValue;
        }



        /// <summary>
        /// Parses given hostname and protocol combination, trying to get out the hostname and protocol as separate entities.
        /// </summary>
        /// <param name="original">typically http://myserver</param>
        /// <param name="delimiter">Typically //</param>
        /// <param name="HostName">typically servername or myserver</param>
        /// <param name="protocol">This would be the http:// or https:// or sts:// part</param>
        /// <param name="alsoSetProperties">If selected, a successful parse will function for setting the target instances protocol and hostname values</param>
        /// <returns>true if successful, false if not</returns>
        public bool splitHostnameAndProtocol(string original, string delimiter, out string HostName, out string protocol, bool alsoSetProperties)
        {
            bool retVal = false; //plan to fail
            HostName = original.Substring(original.LastIndexOf(delimiter)+delimiter.Length); //original.delimiter cutting produced //myserver as result
            protocol = original.Substring(0, original.LastIndexOf(delimiter) + delimiter.Length); //same thing, originally without adding delimiter length produced http: instead of http://

            if (HostName.EndsWith("/"))
            {
                HostName = HostName.Replace("/", "");
            }

            if (!string.IsNullOrEmpty(HostName) && !string.IsNullOrEmpty(protocol))
            {
                if (alsoSetProperties) //only set the properties if they really exist and are not null etc... doesnt guarantee quality, but hey future protocols....
                {
                    _hostname = HostName;
                    _protocol = protocol;
                }

                retVal = true;
            }
            return retVal;

        }
        #endregion
    }
   
    /// <summary>
    /// Server type enumeration for known and expected server types.
    /// </summary>
    public enum ServerType
    {
        //Domino, Exchange, Sharepoint, WSS
        Undetermined,
        DominoUndetermined,
        Domino4,
        Domino5,
        Domino6,
        Domino7,
        Domino8,
        ExchangeUndetermined,
        Exchange4,
        Exchange5,
        Exchange2000,
        Exchange2003,
        Exchange2007,
        Exchange2010,
        WSSUndetermined,
        WSSSTS,
        WSS2,
        WSS3,
        SharepointUndetermined,
        SharepointSPS,
        Sharepoint2003,
        Sharepoint2007,
        Sharepoint2010

    }

}
